home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / COMM / ANSI_133 / MUSICA.PAS < prev    next >
Pascal/Delphi Source File  |  1990-06-13  |  14KB  |  475 lines

  1. { $A+,B-,D-,E-,F+,I-,L-,O+,R-,S-,V-}
  2. (*
  3.     Musica v1.01 (c) CopyRight P.H.Rankin Hansen 1990.
  4.  
  5.     This unit implements the Play  statement knovn from Basic in Turbo
  6.     Pascal  versions  5.x  and  higher.  (version  4  does not support
  7.     procedural types). The syntax adhers  to the Basic syntax with the
  8.     exception  of the  X command,  wich has  no meaning  in a compiled
  9.     language.
  10.  
  11.     Released in Denmark on June 3rd, 1990 as part of PingAnsi 1.30.
  12.  
  13.     By  using this  material You  assume FULL  responsibility for  ANY
  14.     consequences - direct or indirect - thereof. Any dispute regarding
  15.     this  material shall  be setteled  by Danish  law and  in a Danish
  16.     Court.
  17.  
  18.      (Sigh!)
  19.  
  20.     This source  may NOT be  used by Lawyers,  Politicians or, persons
  21.     engaged  in any  other form  of terrorism.  Otherwise the usage is
  22.     free.
  23.  
  24.     This  source  may  be  freely  distributed  as  long  as no fee is
  25.     charged.
  26.  
  27.     Please direct any comments, corrections, modifications via netmail
  28.     to:
  29.  
  30.                       Ping Hansen - Fido Net 2:231/62.58
  31.  
  32. *)
  33. Unit Musica;
  34.  
  35. Interface
  36.  
  37. Uses Dos, TpCrt;{CRT will do as well}
  38.  
  39. Const
  40.   MaxPlayBuffer       = 64;
  41.   { set this to true to disable background processing of sound }
  42.   NoBackground        : Boolean = False;
  43.   { If this is set stuff will WAIT for room in play buffer before returning }
  44.   WaitForSpace        : Boolean = True;
  45.  
  46. Var
  47.   BackGroundPlayHook  : Procedure(Tone, Duration : Word);
  48.   PlayBuffer          : Array[0..MaxPlayBuffer] Of
  49.     Record
  50.       Tone,
  51.       Duration            : Word;
  52.     End;
  53.  
  54. Procedure Play(St : String);
  55. Procedure PurgePlayBuffer;
  56. Function PlayBufferEmpty : Boolean;
  57. Function PlayBufferFull : Boolean;
  58.   {$F+}
  59. Procedure Stuff(Tone, Time : Word);
  60.   {$F-}
  61. Function GrabTimer  : Boolean;
  62.   {$F+}
  63. Procedure ReleaseTimer;
  64.   {$F-}
  65.  
  66.   {-----------------------------------------------------------------------}
  67.  
  68. Implementation
  69.  
  70. Const
  71.   Timer0              = 0;
  72.   FirstPlay           : Word = 0; { buffer Pointer }
  73.   LastPlay            : Word = 1; { buffer Pointer }
  74.   TimerMode           : Byte = 0; { saved mode for the timer }
  75.  
  76. Var
  77.   SaveExitProc        : Pointer;
  78.   SaveTimerInt        : Pointer;
  79.  
  80.   {-----------------------------------------------------------------------}
  81.  
  82.   Procedure Play(St : String);
  83.  
  84.   Const
  85.     Notes               : Array[1..84] Of Word =
  86.     { C    C#,D-  D    D#,E-  E     F    F#,G-  G    G#,A-  A    A#,B-  B  }
  87.     (0065, 0070, 0073, 0078, 0082, 0087, 0093, 0098, 0104, 0110, 0117, 0123,
  88.      0131, 0139, 0147, 0156, 0165, 0175, 0185, 0196, 0208, 0220, 0233, 0247,
  89.      0262, 0277, 0294, 0311, 0330, 0349, 0370, 0392, 0415, 0440, 0466, 0494,
  90.      0523, 0554, 0587, 0622, 0659, 0698, 0740, 0784, 0831, 0880, 0932, 0987,
  91.      1047, 1109, 1175, 1245, 1329, 1397, 1480, 1568, 1661, 1760, 1865, 1976,
  92.      2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
  93.      4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902);
  94.     MusicType           : Byte = 7; {Normal - note plays for 7/8 of time}
  95.     Tempo               : Word = 120; {120 beats per minute}
  96.     StdNoteLength       : Word = 4; {Quarter note}
  97.     Octave              : Word = 3; {Third octave}
  98.     BackGround          : Boolean = False; {Mn is default}
  99.  
  100.   Var
  101.     PlayTime, IdleTime,
  102.     DotTime, TempTime,
  103.     NoteLength, Note,
  104.     Index               : Word;
  105.     Ch                  : Char;
  106.  
  107.     {-------------}
  108.  
  109.     Function Numerical(Var Index : Word) : Word;
  110.  
  111.     Var
  112.       n                   : Word;
  113.     Begin
  114.       n := 0;
  115.       While (Index <= Length(St)) And (St[Index] In ['0'..'9']) Do
  116.       Begin
  117.         n := n * 10 + Ord(St[Index]) - Ord('0');
  118.         Inc(Index)
  119.       End;
  120.       Numerical := n;
  121.     End {Numerical} ;
  122.  
  123.     {-------------}
  124.  
  125.     Procedure CheckDots(Var Index : Word);
  126.  
  127.     Begin
  128.       While (Index <= Length(St)) And ((St[Index] = '.') Or (St[Index] = ',')) Do
  129.       Begin
  130.         DotTime := DotTime + DotTime Div 2;
  131.         Inc(Index)
  132.       End;
  133.     End {CheckDots} ;
  134.  
  135.     {-------------}
  136.  
  137.   Begin                           {Play subroutine}
  138.     Index := 1;
  139.     While Index < Length(St) Do
  140.     Begin
  141.       NoteLength := StdNoteLength;
  142.       DotTime := 1000;
  143.       Ch := Upcase(St[Index]);
  144.       Case Ch Of
  145.         'A'..'G' :
  146.           Begin                   {read note}
  147.             Note := Pos(Ch, 'CcDdEFfGgAaB');
  148.             Inc(Index);
  149.  
  150.             {Check for sharp or flat}
  151.             If Index <= Length(St) Then
  152.               Case St[Index] Of
  153.                 '#', '+' :
  154.                   Begin
  155.                     Inc(Note);
  156.                     Inc(Index);
  157.                   End;
  158.                 '-' :
  159.                   Begin
  160.                     Dec(Note);
  161.                     Inc(Index);
  162.                   End;
  163.               End;
  164.  
  165.             {Check for length suffix}
  166.             If (Index <= Length(St)) And
  167.             (St[Index] In ['0'..'9']) Then
  168.             Begin
  169.               NoteLength := Numerical(Index);
  170.             End;
  171.             CheckDots(Index);
  172.  
  173.             {calculate periods}
  174.             TempTime := Round(DotTime / Tempo / NoteLength * 240);
  175.             PlayTime := Round(TempTime * MusicType / 8);
  176.             IdleTime := TempTime - PlayTime;
  177.  
  178.             {Play the note}
  179.             If BackGround
  180.             Then
  181.             Begin
  182.               BackGroundPlayHook(Notes[Note + Octave * 12], PlayTime);
  183.               If IdleTime <> 0 Then BackGroundPlayHook(0, IdleTime);
  184.             End
  185.             Else
  186.             Begin
  187.               Sound(Notes[Note + Octave * 12]);
  188.               Delay(PlayTime);
  189.               If IdleTime <> 0 Then
  190.               Begin
  191.                 NoSound;
  192.                 Delay(IdleTime)
  193.               End;
  194.             End;
  195.           End;
  196.         '<' :
  197.           Begin                   {step octave down}
  198.             If Octave > 0 Then Dec(Octave);
  199.             Inc(Index);
  200.           End;
  201.         '>' :
  202.           Begin                   {step octave up}
  203.             If Octave < 6 Then Inc(Octave);
  204.             Inc(Index);
  205.           End;
  206.         'L' :
  207.           Begin                   {set notelength}
  208.             Inc(Index);
  209.             StdNoteLength := Numerical(Index);
  210.             If (StdNoteLength < 1) Or (StdNoteLength > 64) Then
  211.               StdNoteLength := 4;
  212.           End;
  213.         'M' :
  214.           Begin                   {determine music type}
  215.             Inc(Index);
  216.             If (Index <= Length(St)) Then
  217.             Begin
  218.               Case Upcase(St[Index]) Of
  219.                 'S' : MusicType := 6; {music staccato}
  220.                 'N' : MusicType := 7; {music normal}
  221.                 'L' : MusicType := 8; {music legato}
  222.                 'B' : BackGround := True; {enable background buffering}
  223.                 'F' : BackGround := False; {disable do.}
  224.               End;
  225.               Inc(Index);
  226.             End;
  227.           End;
  228.         'O' :
  229.           Begin                   {set octave}
  230.             Inc(Index);
  231.             Octave := Numerical(Index);
  232.             If Octave > 6 Then Octave := 6;
  233.           End;
  234.         'P' :
  235.           Begin                   {pause}
  236.             NoSound;
  237.             Inc(Index);
  238.             NoteLength := Numerical(Index);
  239.             If (NoteLength < 1) Or (NoteLength > 64) Then
  240.               NoteLength := StdNoteLength;
  241.             CheckDots(Index);
  242.  
  243.             {calculate pause}
  244.             IdleTime := DotTime Div Tempo * (240 Div NoteLength);
  245.  
  246.             {execute pause}
  247.             If BackGround
  248.             Then BackGroundPlayHook(0, IdleTime)
  249.             Else Delay(IdleTime);
  250.           End;
  251.         'T' :
  252.           Begin                   {set tempo}
  253.             Inc(Index);
  254.             Tempo := Numerical(Index);
  255.             If (Tempo < 32) Or (Tempo > 255) Then
  256.               Tempo := 120;
  257.           End;
  258.         'N' :
  259.           Begin                   {play note #nn}
  260.             Inc(Index);
  261.             Note := Numerical(Index);
  262.             If (Note < 1) Then Note := 1;
  263.             If (Note > 84) Then Note := 84;
  264.             CheckDots(Index);
  265.  
  266.             {calculate periods}
  267.             TempTime := Round(DotTime / Tempo / NoteLength * 240);
  268.             PlayTime := Round(TempTime * MusicType / 8);
  269.             IdleTime := TempTime - PlayTime;
  270.  
  271.             {Play the note}
  272.             If BackGround
  273.             Then
  274.             Begin
  275.               BackGroundPlayHook(Notes[Note + Octave * 12], PlayTime);
  276.               If IdleTime <> 0 Then BackGroundPlayHook(0, IdleTime);
  277.             End
  278.             Else
  279.             Begin
  280.               Sound(Notes[Note + Octave * 12]);
  281.               Delay(PlayTime);
  282.               If IdleTime <> 0 Then
  283.               Begin
  284.                 NoSound;
  285.                 Delay(IdleTime)
  286.               End;
  287.             End;
  288.           End;
  289.         Else                      {garbage collector}
  290.           Inc(Index);             {pollution, Just dump it}
  291.       End;
  292.     End {While} ;
  293.     NoSound;                      {we are finished}
  294.   End {Play} ;
  295.  
  296.   {-----------------------------------------------------------------------}
  297.  
  298.   {$F+}
  299.   Procedure DummyStuff(Tone, Duration : Word);
  300.     {$F-}
  301.     {dummy background}
  302.   Begin
  303.     If Tone <> 0
  304.     Then Sound(Tone)
  305.     Else NoSound;
  306.     Delay(Duration);
  307.   End {DummyStuff} ;
  308.  
  309.   {-------------------------------------------------------------------------}
  310.  
  311.   Procedure PurgePlayBuffer;
  312.  
  313.   Begin
  314.     Inline($FA); {CLI}
  315.     FillChar(PlayBuffer, SizeOf(PlayBuffer), 0);
  316.     FirstPlay := 0;
  317.     LastPlay := 1;
  318.     Inline($FB); {STI}
  319.   end {PurgePlayBuffer} ;
  320.  
  321.   {-------------------------------------------------------------------------}
  322.  
  323.   Function PlayBufferEmpty : Boolean;
  324.  
  325.   Begin
  326.     PlayBufferEmpty := (FirstPlay = LastPlay);
  327.   End {PlayBufferEmpty} ;
  328.  
  329.   {-------------------------------------------------------------------------}
  330.  
  331.   Function PlayBufferFull : Boolean;
  332.  
  333.   Begin
  334.     PlayBufferFull := (LastPlay = FirstPlay - 1) Or
  335.     ((LastPlay = MaxPlayBuffer) And (FirstPlay = 1));
  336.   End {PlayBufferFull} ;
  337.  
  338.   {-------------------------------------------------------------------------}
  339.  
  340.   {$F+}
  341.   Procedure Stuff(Tone, Time : Word);
  342.     {$F-}
  343.  
  344.     { Place a note in background buffer. }
  345.  
  346.   Begin
  347.     If NoBackground Then
  348.     Begin
  349.       If Tone <> 0 Then Sound(Tone);
  350.       Delay(Time);
  351.       Exit;
  352.     End;
  353.     While WaitForSpace And PlayBufferFull Do {} ;
  354.     If                            {(LastPlay <> FirstPlay - 1) And
  355.     ((LastPlay <> MaxPlayBuffer) Or (FirstPlay <> 1))} Not PlayBufferFull Then
  356.     Begin
  357.       PlayBuffer[LastPlay].Tone := Tone;
  358.       PlayBuffer[LastPlay].Duration := Time;
  359.       Inc(LastPlay);
  360.       If LastPlay > MaxPlayBuffer Then LastPlay := 1;
  361.     End;
  362.   End {Stuff} ;
  363.  
  364.   {-------------------------------------------------------------------------}
  365.  
  366.   Procedure InitTimer(Timer, Mode : Byte; Count : Word);
  367.  
  368.   Var
  369.     Tics                : LongInt Absolute $40 : $6C;
  370.     t                   : LongInt;
  371.  
  372.   Begin
  373.     t := Tics;
  374.     While t = Tics Do {} ;        { wait for clock tick }
  375.     Inline($FA);                  {CLI}
  376.     Port[$43] := Mode;
  377.     Port[$40 + Timer] := Lo(Count);
  378.     Port[$40 + Timer] := Hi(Count);
  379.     Inline($FB);                  {STI}
  380.   End;
  381.  
  382.   {-------------------------------------------------------------------------}
  383.  
  384.   Procedure NewTimer(BP : Word); Interrupt;
  385.  
  386.   Const
  387.     InTune              : Boolean = True;
  388.     TimerVar            : Word = 54; { no delay first time }
  389.     Count               : Word = 05;
  390.   Begin
  391.     Inc(TimerVar);
  392.     If TimerVar >= 55 Then
  393.     Begin
  394.       TimerVar := 0;
  395.       Inline($9C / $FF / $1E / SaveTimerInt); { Pushf/Call Far SaveTimer }
  396.     End
  397.     Else
  398.     Begin
  399.       Port[$20] := $20;           { Non speciffic EOI }
  400.     End;
  401.     Inline($FB);                  {STI}
  402.     If Count > 0 Then Dec(Count);
  403.     If Count = 0 Then
  404.     Begin
  405.       If InTune Then
  406.       Begin
  407.         InTune := False;
  408.         NoSound;
  409.       End;
  410.       If (LastPlay <> FirstPlay) Then
  411.       Begin
  412.         If (PlayBuffer[FirstPlay].Tone <> 0) Then
  413.         Begin
  414.           Sound(PlayBuffer[FirstPlay].Tone);
  415.           InTune := True;
  416.         End;
  417.         If (PlayBuffer[FirstPlay].Duration <> 0)
  418.         Then Count := PlayBuffer[FirstPlay].Duration;
  419.         Inc(FirstPlay);
  420.         If FirstPlay > MaxPlayBuffer Then FirstPlay := 1;
  421.       End;
  422.     End;
  423.   End {NewTimer} ;
  424.  
  425.   {-------------------------------------------------------------------------}
  426.  
  427.   {$F+}
  428.   Procedure ReleaseTimer;
  429.     {$F-}
  430.  
  431.     { unload the interrupt handler }
  432.  
  433.   Begin
  434.     { Reprogram the 8253 to a 55 ms period }
  435.     InitTimer(Timer0, $36, 0);
  436.     SetIntVec($8, SaveTimerInt);
  437.     ExitProc := SaveExitProc;
  438.     NoSound;
  439.     BackgroundPlayHook := DummyStuff;
  440.   End {ReleaseTimer} ;
  441.  
  442.   {-------------------------------------------------------------------------}
  443.  
  444.   Function GrabTimer  : Boolean;
  445.  
  446.   Begin
  447.     GrabTimer := True;
  448.     FillChar(PlayBuffer, SizeOf(PlayBuffer), 0);
  449.     GetIntVec($8, SaveTimerInt);
  450. (*
  451.   Port[$43] := $E2;        { readback command. Timer 0, status. }
  452.   TimerMode := Port[$40] And $0F + $30;
  453.   if (TimerMode <> $36)
  454.   then GrabTimer := False
  455.   else
  456. *)
  457.     Begin
  458.       SaveExitProc := ExitProc;
  459.       InitTimer(Timer0, $36, $04A8);
  460.       SetIntVec($8, @NewTimer);
  461.       SaveExitProc := ExitProc;
  462.       ExitProc := @ReleaseTimer;
  463.       BackgroundPlayHook := Stuff;
  464.     (*
  465.     Stuff(10, 100); {void attempt to fix problem with first note}
  466.     *)
  467.     End;
  468.   End {GrabTimer} ;
  469.  
  470.   {-----------------------------------------------------------------------}
  471.  
  472. Begin
  473.   BackGroundPlayHook := DummyStuff;
  474. End.
  475.